﻿using System;
using System.Collections.Generic;
using Lilium.LParse;

namespace BBParser
{
    enum BBFormatting
    {
        Bold,
        Italic,
        Underline
    }

    abstract class BBNode
    {
        protected BBNode()
        {
            ChildNodes = new BBNode[0];
        }

        protected BBNode(ICollection<BBNode> childNodes)
        {
            ChildNodes = childNodes;
        }

        public readonly ICollection<BBNode> ChildNodes;
    }

    class BBFormattingNode : BBNode
    {
        public BBFormattingNode(BBFormatting formatting, ICollection<BBNode> childNodes)
            : base(childNodes)
        {
            Formatting = formatting;
        }

        public readonly BBFormatting Formatting;
    }

    class BBTextNode : BBNode
    {
        public BBTextNode(string text)
            : base()
        {
            Text = text;
        }

        public readonly string Text;
    }

    class Program
    {
        static Parser<char, BBNode> BBTag = ParserCombinators.Choise(
            CreateBBFormattingTagParser("b", BBFormatting.Bold).Try(),
            CreateBBFormattingTagParser("i", BBFormatting.Italic).Try(),
            CreateBBFormattingTagParser("u", BBFormatting.Underline).Try())
            .Memoize();

        static Parser<char, BBNode> BBText = TextParsers.Many1Till(BBTag.LookAhead().OrEof()).Select(text => (BBNode)new BBTextNode(text));

        static Parser<char, BBNode> BBNode = BBTag.Or(BBText);

        static Parser<char, ICollection<BBNode>> BBNodes = ParserCombinators.Many(BBNode);

        static Parser<char, BBNode> CreateBBFormattingTagParser(string tag, BBFormatting formatting)
        {
            var openTagParser = TextParsers.Word(tag).Between(TextParsers.Char('['), TextParsers.Char(']'));
            var closeTagParser = TextParsers.Word(tag).Between(TextParsers.Word("[/"), TextParsers.Char(']'));

            var bbNodesTill = new Lazy<Parser<char, ICollection<BBNode>>>(() =>
                    BBTag
                .Or(TextParsers.Many1Till(BBTag.LookAhead().Pure("").Or(closeTagParser.LookAhead())).Select(text => (BBNode)new BBTextNode(text)))
                .Many());

            return from open in openTagParser
                   from childNodes in bbNodesTill.Value
                   from close in closeTagParser
                   select (BBNode)new BBFormattingNode(formatting, childNodes);
        }

        static void Main(string[] args)
        {
            var test1 = BBNodes.Parse("BB [b]Codes [i]test[/i][/b]");
            var test2 = BBNodes.Parse("[b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b]test[/b]");
        }
    }
}